using System;

namespace SeaVM.Instructions
{
    public class Cjmp
    {
        public static Instruction Create(int line)
        {
            Instruction inst = new Instruction();
            inst.opcode = Opcode.Cjmp;
            InstructionArg arg1 = new InstructionArg();
            arg1.type = InstructionArgType.Number;
            arg1.number = line;
            inst.args.Add(arg1);
            return inst;
        }
        public static Instruction Create(string jump_node)
        {
            Instruction inst = new Instruction();
            inst.opcode = Opcode.Cjmp;
            InstructionArg arg1 = new InstructionArg();
            arg1.type = InstructionArgType.JumpNode;
            arg1.jump_node = jump_node;
            inst.args.Add(arg1);
            return inst;
        }

        public static Instruction Create2(int address)
        {
            Instruction inst = new Instruction();
            inst.opcode = Opcode.Cjmp;
            InstructionArg arg1 = new InstructionArg();
            arg1.type = InstructionArgType.Address;
            arg1.address = address;
            inst.args.Add(arg1);
            return inst;
        }
        public static Instruction Create2(string define)
        {
            Instruction inst = new Instruction();
            inst.opcode = Opcode.Cjmp;
            InstructionArg arg1 = new InstructionArg();
            arg1.type = InstructionArgType.Define;
            arg1.define = define;
            inst.args.Add(arg1);
            return inst;
        }

        public static bool IsJump(VM vm)
        {
            Data top = vm.stack.Peek();
            if(top.type != DataType.Boolean) return false;
            return top.boolean.value;
        }

        public static void Run(VM vm,Instruction inst)
        {
            InstructionArg arg1 = inst.args[0];
            if(vm.stack.Count <= 0)
            {
                throw new VmRunError("栈为空！无法进行判断");
            }
            if(IsJump(vm))
            {
                switch(arg1.type)
                {
                    case InstructionArgType.Number:
                    {
                        Jmp.jmp(vm,arg1.number);
                        break;
                    }
                    case InstructionArgType.JumpNode:
                    {
                        Jmp.jmp(vm,arg1.jump_node);
                        break;
                    }
                    case InstructionArgType.Address:
                    {
                        if(arg1.address < 0 || arg1.address >= vm.memory.size)
                        {
                            throw new VmRunError("地址越界");
                        }
                        Data data = vm.memory.Read(arg1.address);
                        if(ReferenceEquals(data,null))
                        {
                            throw new VmRunError("地址处为Nil");
                        }
                        if(data.type == DataType.Number)
                        {
                            Jmp.jmp(vm,(int)data.number.value);
                            break;
                        }
                        if(data.type == DataType.String)
                        {
                            Jmp.jmp(vm,data.@string.value);
                            break;
                        }
                        throw new VmRunError("地址处类型需为Number或者String");
                    }
                    case InstructionArgType.Define:
                    {
                        if(vm.defines.ContainsKey(arg1.define))
                        {
                            int address = vm.defines[arg1.define];
                            if(address < 0 || address >= vm.memory.size)
                            {
                                throw new VmRunError("地址越界");
                            }
                            Data data = vm.memory.Read(address);
                            if(ReferenceEquals(data,null))
                            {
                                throw new VmRunError("地址处为Nil");
                            }
                            if(data.type == DataType.Number)
                            {
                                Jmp.jmp(vm,(int)data.number.value);
                                break;
                            }
                            if(data.type == DataType.String)
                            {
                                Jmp.jmp(vm,data.@string.value);
                                break;
                            }
                        }
                        else
                        {
                            throw new VmRunError("没有这个定义");
                        }
                        break;
                    }
                    default:throw new VmRunError("没有这种形式的指令");
                }
            }
            else
            {
                vm.line++;
            }
        }
    }
}